package com.example.pre.tools;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.example.pre.exception.ServiceException;

/**
 * 
 * Copyright 2021 四川澳沙科技有限公司
 * 
 * @ClassName: Tools.java
 * @Description: 通用工具类
 * @version: v1.0.0
 * @author: hepan
 * @date: 2021年8月12日-上午9:35:41
 */
public class Tools {

	@Deprecated
	private static Map<String, String> MAP;

	/**
	 * 找出两个数组中不相同的元素
	 * 
	 * @param t1
	 * @param t2
	 * @return
	 */
	public static <T> List<T> compare(T[] t1, T[] t2) {
		List<T> list1 = Arrays.asList(t1);
		List<T> list2 = new ArrayList<T>();
		for (T t : t2) {
			if (!list1.contains(t)) {
				list2.add(t);
			}
		}
		return list2;
	}

	/**
	 * 返回一个10000以内的随机Message_id
	 * 
	 * @return
	 */
	public static int MessageRandom() {
		return (int) (Math.random() * 10000);
	}

	/**
	 * 进行编码 utf转iso-8859-1
	 * 
	 * @param str
	 * @return
	 */
	public static String httpEncoding(String str) {
		if (str != null)
			try {
				str = URLEncoder.encode(str, "utf-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		return str;
	}

	/**
	 * 获取在当前时期上加减天数的日期
	 * 
	 * @param addDay
	 * @return
	 */
	public static Long getAddDate(int addDay) {
		Calendar date = Calendar.getInstance();
		date.set(Calendar.DAY_OF_YEAR, date.get(Calendar.DAY_OF_YEAR) + addDay);
		return date.getTimeInMillis();

	}

	/**
	 * 读取jdpc配置文件
	 */
	@Deprecated
	public static void readIndexProperties() {

		try {
			Properties properties = new Properties();
			InputStream resourceAsStream = Tools.class.getClassLoader().getResourceAsStream("config/jdbc.properties");
			properties.load(resourceAsStream);
			Set<String> keys = properties.stringPropertyNames();

			for (String key : keys) {
				MAP.put(key, properties.getProperty(key));
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取配置文件里指定key的value
	 * 
	 * @param key
	 * @return
	 */
	@Deprecated
	public static String getProperty(String key) {
		if (MAP == null) {
			MAP = new HashMap<String, String>();
			readIndexProperties();
		}
		return MAP.get(key);
	}

	/**
	 * 将一个时间对象转换为指定格式的字符串
	 * 
	 * @param date   时间对象
	 * @param format 时间格式
	 * @return
	 */
	public static String dateFormat(Object date, String format) {
		return new SimpleDateFormat(format).format(date);
	}

	/**
	 * 生成随机字符串
	 * 
	 * @return
	 */
	public static String createUUID() {
		String s = UUID.randomUUID().toString().replaceAll("-", "");
		return s;
	}

	/**
	 * BASE64解密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptBASE64(String key) throws Exception {
		return Base64.getDecoder().decode(key);
	}

	/**
	 * BASE64加密
	 * 
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encryptBASE64(byte[] key) throws Exception {
		return Base64.getEncoder().encodeToString(key);
	}

	/**
	 * 去除字符串末尾编码编号小于等于32(' ')的字符
	 * 
	 * @param value
	 * @return
	 */
	public static String trimEnd(String value) {
		char[] charArray = value.toCharArray();
		int len = charArray.length;
		while ((0 < len) && (charArray[len - 1] <= ' ')) {
			len--;
		}
		return (len < charArray.length) ? value.substring(0, len) : value;
	}

	/**
	 * 重写equals 忽略" \t\n\r\f"等字符
	 * 
	 * @param value1
	 * @param value2
	 * @return
	 */
	public static boolean equals(String value1, String value2) {
		StringTokenizer token1 = new StringTokenizer(value1);
		StringTokenizer token2 = new StringTokenizer(value2);
		StringBuffer sb = new StringBuffer();
		while (token1.hasMoreTokens()) {
			sb.append(token1.nextToken());
		}
		String val1 = sb.toString();
		sb = new StringBuffer();
		while (token2.hasMoreTokens()) {
			sb.append(token2.nextToken());
		}
		String val2 = sb.toString();
		return val1.equals(val2);
	}

	/**
	 * 重写trim 去除全部空格字符
	 * 
	 * @param value
	 * @return
	 */
	public static String trim(String value) {
		char[] charArray = value.toCharArray();
		StringBuffer sb = new StringBuffer();
		for (char ch : charArray) {
			if (ch > ' ') {
				sb.append(ch);
			}
		}
		return sb.toString();
	}

	/**
	 * 尝试ping主机
	 * 
	 * @param hostIp
	 * @return
	 * @throws IOException
	 */
	public static boolean ping(String hostIp) {
		int timeOut = 3000; // 超时应该在3钞以上
		try {
			boolean status = InetAddress.getByName(hostIp).isReachable(timeOut); // 当返回值是true时，说明host是可用的，false则不可。
			return status;
		} catch (Exception e) {
		}
		return false;
	}

	/**
	 * 检测文件重名 自动重命名
	 * 
	 * @param saveZipName 需要检测的文件名集合
	 * @param fileName    当前文件名
	 * @param count       重名次数 调用这个方法时这里默认传1
	 * @return 如果有重名会在名字后面加1 列如： test123.txt、test123(1).txt、test123(2).txt
	 */
	public static String checkName(List<String> saveZipName, String fileName, int count) {
		boolean exist = false; // 默认名字不重名
		for (int i = 0; i < saveZipName.size(); i++) {
			if (fileName.equals(saveZipName.get(i))) {
				exist = true;
				break;
			}
		}
		if (exist) { // 如果重名重新生成个名字
			// 处理原先的名字 将后缀名字去掉
			int endIndex = fileName.lastIndexOf(".");
			if (endIndex > 0 && (endIndex + 1) < fileName.length()) { // 文件名字存在后缀名 不以点开头 也不以点结尾
				String fileNameLeftTemp = fileName;
				String fileNameRightTemp = fileName;
				String fileNameTempLeft = fileNameLeftTemp.substring(0, endIndex); // 文件名字
				String fileNameTempRight = fileNameRightTemp.substring(endIndex + 1, fileName.length()); // 文件后缀名字
				if (fileNameTempLeft.lastIndexOf("(") < 0 || fileNameTempLeft.lastIndexOf(")") < 0
						|| (fileNameTempLeft.length() - 1) > fileNameTempLeft.lastIndexOf(")")
						|| (fileNameTempLeft.lastIndexOf(")") - fileNameTempLeft.lastIndexOf("(")) <= 1) { // 文件名字没有(2)的形式
					fileName = checkName(saveZipName, fileNameTempLeft + "(" + count + ")." + fileNameTempRight,
							++count); // 递归生成文件名字
				} else { // 文件已经存在 adb(1).doc或者 adb(i).doc 的形式
					// 取出 1 或者 i 等字符串 实现自增
					String fileNameTempLeftString = fileNameTempLeft;
					String numCountString = fileNameTempLeftString.substring(
							fileNameTempLeftString.lastIndexOf("(") + 1, fileNameTempLeftString.lastIndexOf(")"));
					int numCount = -1;
					try {
						numCount = Integer.parseInt(numCountString);
					} catch (Exception e) {
						numCount = -1;
					}
					String realName = fileNameTempLeft.substring(0, fileNameTempLeft.lastIndexOf("("));
					if (numCount > 1) { // 是数字，不是字符串，截取(x)之前的字符 在numCount这个数字的基础上实现自增
						fileName = checkName(saveZipName, realName + "(" + ++numCount + ")." + fileNameTempRight, 2); // 递归生成文件名字
					} else { // 是字符串
						fileName = checkName(saveZipName, realName + "(" + count + ")." + fileNameTempRight, ++count); // 递归生成文件名字
					}
				}
			} else { // 文件不存在后缀名字（以点结尾也是不存在）
				String fileNameTempLeft = fileName;
				if (fileNameTempLeft.lastIndexOf("(") < 0 || fileNameTempLeft.lastIndexOf(")") < 0
						|| (fileNameTempLeft.length() - 1) > fileNameTempLeft.lastIndexOf(")")
						|| (fileNameTempLeft.lastIndexOf(")") - fileNameTempLeft.lastIndexOf("(")) <= 1) { // 文件名字没有(2)的形式
					fileName = checkName(saveZipName, fileNameTempLeft + "(" + count + ")", ++count); // 递归生成文件名字
				} else { // 文件已经存在 adb(1)或者 adb(i)的形式
					// 取出 1 或者 i 等字符串 实现自增
					String fileNameTempLeftString = fileNameTempLeft;
					String numCountString = fileNameTempLeftString.substring(
							fileNameTempLeftString.lastIndexOf("(") + 1, fileNameTempLeftString.lastIndexOf(")"));
					int numCount = -1;
					try {
						numCount = Integer.parseInt(numCountString);
					} catch (Exception e) {
						numCount = -1;
					}
					String realName = fileNameTempLeft.substring(0, fileNameTempLeft.lastIndexOf("("));
					if (numCount > 1) { // 是数字，不是字符串，截取(x)之前的字符 在numCount这个数字的基础上实现自增
						fileName = checkName(saveZipName, realName + "(" + ++numCount + ")", 2); // 递归生成文件名字
					} else { // 是字符串
						fileName = checkName(saveZipName, realName + "(" + count + ")", ++count); // 递归生成文件名字
					}
				}
			}
		}
		return fileName;
	}

	/**
	 * 获取文件大小并转换为自适应单位
	 * 
	 * @param fileLength 文件字节数
	 * @return
	 */
	public static String getFileSize(long fileLength) {
		int unit = 0; // 单位序号
		double s = fileLength; // 余数
		// 最大单位设置为TB
		for (int i = 1; i < 5; i++) {
			if (s < 1024) {
				break;
			}
			unit = i;
			s /= 1024;
		}
		String result;
		switch (unit) {
		case 0:
			// 最佳显示单位B
			result = String.format("%.0f %s", s, "B");
			break;
		case 1:
			// 最佳显示单位KB
			result = String.format("%.0f %s", s, "KB");
			break;
		case 2:
			// 最佳显示单位MB，保留小数点最后一位
			result = String.format("%.1f %s", s, "MB");
			break;
		case 3:
			// 最佳显示单位GB,保留小数点后两位
			result = String.format("%.2f %s", s, "GB");
			break;
		default:
			// 最佳显示单位TB,保留小数点后三位
			result = String.format("%.3f %s", s, "TB");
		}
		return result;
	}

	/**
	 * 根据传入的后缀查类型
	 * 
	 * @param suffix 文件后缀
	 * @return 文件类型
	 */
	@Deprecated
	public static String queryResourcesType(String suffix) {
		if (suffix == null) {
			return "others";
		}
		if (!suffix.equals("")) {
			suffix = suffix.toLowerCase();
			String[] videos = Tools.getProperty("video").split("@");
			String[] words = Tools.getProperty("word").split("@");
			String[] audios = Tools.getProperty("audio").split("@");
			String[] images = Tools.getProperty("image").split("@");
			// 判断是否是视频文件
			for (String str : videos) {
				if (suffix.equals(str))
					return "video";
			}
			for (String str : words) {
				if (suffix.equals(str))
					return "word";
			}
			for (String str : audios) {
				if (suffix.equals(str))
					return "audio";
			}
			for (String str : images) {
				if (suffix.equals(str))
					return "image";
			}
		}
		return "others";
	}

	/**
	 * 根据文件名称获取文件后缀
	 * 
	 * @param fileName 文件名称
	 * @return 文件后缀
	 */
	public static String getFileSuffx(String fileName) {
		return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
	}

	/**
	 * @function:从list中随机抽取若干不重复元素
	 *
	 * @param paramList:被抽取list
	 * @param count:抽取元素的个数
	 * @return:由抽取元素组成的新list
	 */
	public static List<Object> getRandomList(List<Object> paramList, int count) {
		if (paramList.size() < count) {
			return paramList;
		}
		Random random = new Random();
		List<Integer> tempList = new ArrayList<Integer>();
		List<Object> newList = new ArrayList<Object>();
		int temp = 0;
		for (int i = 0; i < count; i++) {
			temp = random.nextInt(paramList.size());// 将产生的随机数作为被抽list的索引
			if (!tempList.contains(temp)) {
				tempList.add(temp);
				newList.add(paramList.get(temp));
			} else {
				i--;
			}
		}
		return newList;
	}

	/**
	 * 将时间戳转为对应的的int类型年月日
	 * 
	 * @param time
	 * @return
	 */
	public static HashMap<String, Integer> changeTimeToint(Timestamp time) {
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		long currentStartTime = time.getTime();
		Calendar nowTime = Calendar.getInstance();
		nowTime.setTime(new Date(currentStartTime));
		int year = nowTime.get(Calendar.YEAR);
		int month = nowTime.get(Calendar.MONTH);
		int day = nowTime.get(Calendar.DATE);
		int newmonth = month + 1;
		map.put("year", year);
		map.put("month", newmonth);
		map.put("day", day);
		return map;

	}

	/**
	 * 隐藏手机号的中间四位,适用于11位的电话号码
	 * 
	 * @param phone 原始的电话号码
	 * @return 处理好的电话号码
	 */
	public static String hidePhone(String phone) {
		String start = phone.substring(0, 3);
		String end = phone.substring(7);
		return start + "****" + end;
	}

	/**
	 * 获取随机数
	 *
	 * @return 6位的随机数
	 */
	public static String randNumber() {
		String sources = "0123456789";
		Random rand = new Random();
		StringBuffer flag = new StringBuffer();
		for (int j = 0; j < 6; j++) {
			flag.append(sources.charAt(rand.nextInt(9)) + "");
		}
		return flag.toString();
	}

	/**
	 * id生成器</br>
	 * 如果该id用作主键或者索引，则建议使用雪花算法生成的id, 在分布式数据库结构下也可使用该方法</br>
	 * 如果使用随机字符串作为主键或者索引，数据量大时，在更新数据时，会造成数据数据库的B+树的分裂，影响效率
	 * 
	 * @return 生成的id
	 */
	public static String createId() {
		return createUUID();
	}

	/**
	 * 获取当前时间 yyyy-MM-dd HH:mm:ss 年月日时分秒
	 * 
	 * @return 生成的时间
	 */
	public static String getNowSimpleDate() {
		return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
	}

	/**
	 * 获取HTML的纯文本内容
	 * 
	 * @param htmlStr 带标签的HTNL内容
	 * @return 去掉标签的纯文本数据
	 */
	public static String delHTMLTag(String htmlStr) {
		return htmlStr.replaceAll("</?[a-zA-Z]+[^><]*>", "");
	}

	/**
	 * 对list按大小进行分组
	 * 
	 * @param <T>
	 * @param list 集合列表
	 * @param size 分组大小
	 * @return
	 */
	public static <T> List<List<T>> partition(final List<T> list, final int size) {
		Integer limit = (list.size() + size - 1) / size;
		List<List<T>> mglist = new ArrayList<List<T>>();
		Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
			mglist.add(list.stream().skip(i * size).limit(size).collect(Collectors.toList()));
		});
		return mglist;
	}

	/**
	 * 求两个double值的平均值
	 * 
	 * @param math1 第一个值
	 * @param math2 第二个值
	 * @return
	 * @throws Exception
	 */
	public static double getAvg(double math1, double math2) throws Exception {
		if (math1 < 0 || math2 < 0 || (math1 == 0 && math2 == 0)) {
			throw new ServiceException("求两个double值的平均值时，传入参数有误！", "系统繁忙，参数错误！", -501);
		}
		if (math1 == math2) {
			return math1;
		}
		// 如果想过double值，直接运算会出现精度缺失，比如：两个double, a=1，b=0.2，两个值相加可能会出现1.299999999999的结果
		BigDecimal math1BigDecimal = BigDecimal.valueOf(math1).stripTrailingZeros();
		BigDecimal math2BigDecimal = BigDecimal.valueOf(math2).stripTrailingZeros();
		BigDecimal twoBigDecimal = BigDecimal.valueOf(2D);

		// 取两者的平均值
		BigDecimal addResult = math1BigDecimal.add(math2BigDecimal).stripTrailingZeros();
		BigDecimal avgResult = addResult.divide(twoBigDecimal).stripTrailingZeros();
		// 处理小数位数（利用二分法处理）
		BigDecimal min = math1BigDecimal.compareTo(math2BigDecimal) == -1 ? math1BigDecimal : math2BigDecimal;
		BigDecimal result = dealPoint(math1BigDecimal, math2BigDecimal, min, avgResult, 0, 2, 0);
		return result.doubleValue();
	}

	/**
	 * 处理小数
	 * 
	 * @param math1       第一个值
	 * @param math2       第二个值
	 * @param min         最小值
	 * @param avg         直接计算的平均值
	 * @param locatePoint 小数截取的位置
	 * @param count       avg的小数等分值
	 * @param start       已经检测过的小数的位置
	 * @return
	 * @throws Exception
	 */
	public static BigDecimal dealPoint(BigDecimal math1, BigDecimal math2, BigDecimal min, BigDecimal avg,
			int locatePoint, int count, int start) throws Exception {

		// 平均值的小数位数
		int totalLen = avg.scale();

		// 不存在小数点直接返回
		if (totalLen <= 0) {
			return avg;
		}

		// 第一次处理(2等分)
		if (locatePoint == 0 && count == 2 && totalLen > 1) {
			locatePoint = totalLen / 2;
		}

		if (locatePoint < 0 || count <= 0) {
			throw new ServiceException("处理小数时，传入参数有误！", "系统繁忙，参数错误！", -501);
		}

		// 去掉平均值的指定小数位
		int p = 1;
		if (locatePoint != 0) {
			// 获取一份儿的位数
			p = totalLen / count;
		}
		p = p + start;

		BigDecimal avgTempMax = avg.setScale(p, BigDecimal.ROUND_DOWN);
		BigDecimal avgTempMin = avg.setScale(p - 1, BigDecimal.ROUND_DOWN);

		// 如果均值的总小数位数为1或者0，则直接返回（该情况一定是整数）
		if (totalLen == 1 || totalLen == 0) {
			if (avgTempMin.compareTo(min) == 1) {
				return avgTempMin;
			}
			return avgTempMax;
		}
		if (count > totalLen) {
			throw new ServiceException("处理小数时，计算有误！", "系统繁忙，参数错误！", -501);
		}

		// 截取刚合适（小数少一位的小于等于最小值，小数多一位的大于最小值）
		if (avgTempMin.compareTo(min) < 1 && avgTempMax.compareTo(min) == 1) {
			return avgTempMax;
		}

		// 截取不够(小数少一位的大于最小值, locatePoint前移动)
		if (avgTempMin.compareTo(min) == 1) {
			// 计算的值发生变化，重新定位
			if (start == 0) {
				return dealPoint(math1, math2, min, avgTempMin, 0, 2, start);
			}
			int scale = avgTempMin.scale();
			if (scale < start) {
				throw new ServiceException("处理小数时，计算有误！", "系统繁忙，参数错误！", -501);
			}
			locatePoint = start + (scale - start) / 2;
			return dealPoint(math1, math2, min, avgTempMin, locatePoint, 2, start);
		}

		// 截取多了(小数少一位的大于最小值, locatePoint后移动)
		if (avgTempMax.compareTo(min) < 1) {
			// 等分数翻倍，已经检测过的位置后移，将要检测的位置后移
			count = count * 2;
			if (count > totalLen) {
				// 倍数不可能超过总长度
				count = totalLen;
			}
			start = avgTempMax.scale();
			locatePoint = start + (totalLen - start) / 2;
			return dealPoint(math1, math2, min, avg, locatePoint, count, start);
		}

		throw new ServiceException("处理小数时，计算有误！", "系统繁忙，参数错误！", -501);
	}
//	public static void main(String arg[]) {
//		boolean b = compareQuestion(null, null);
//		System.out.println(b);
//	}

}
